#include "nehefont.h"
#include "error.h"

#include <cstdio>

namespace freetype {

font_data::font_data(const char* fname,unsigned int h){
    this->h=h;
    font_name = fname;
}
font_data::~font_data(){}

inline int next_p2 (int a ){
	int rval=1;
	while(rval<a) rval<<=1;
	return rval;
}

// Create A Display List Corresponding To The Given Character.
bool make_dlist ( FT_Face face, unsigned char ch, GLuint list_base, GLuint * tex_base ) {

	if(FT_Load_Glyph( face, FT_Get_Char_Index( face, ch ), FT_LOAD_DEFAULT )){
        error::log(error::con_only,"error\n");
        error::log(error::log_err,"font glyph %c(%d): error loading glyph",ch,ch);
        return false;
	}

	FT_Glyph glyph;
	if(FT_Get_Glyph( face->glyph, &glyph )){
        error::log(error::con_only,"error\n");
        error::log(error::log_err,"font glyph %c(%d): error getting glyph",ch,ch);
        return false;
	}

	if(FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 )){
        error::log(error::con_only,"error\n");
        error::log(error::log_err,"font glyph %c(%d): error in bitmap conversion",ch,ch);
        return false;
	}

	FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

	FT_Bitmap& bitmap=bitmap_glyph->bitmap;

	int width = next_p2( bitmap.width );
	int height = next_p2( bitmap.rows );

	GLubyte* expanded_data = new GLubyte[ 2 * width * height];

	for(int j=0; j <height;j++) {
		for(int i=0; i < width; i++){
			expanded_data[2*(i+j*width)]= expanded_data[2*(i+j*width)+1] =
				(i>=bitmap.width || j>=bitmap.rows) ?
				0 : bitmap.buffer[i + bitmap.width*j];
		}
	}

	glBindTexture( GL_TEXTURE_2D, tex_base[ch]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

	glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
		GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );

	delete [] expanded_data;

	glNewList(list_base+ch,GL_COMPILE);

	glBindTexture(GL_TEXTURE_2D,tex_base[ch]);

	glPushMatrix();

	glTranslatef(bitmap_glyph->left,0,0);

	glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);

	float   x=(float)bitmap.width / (float)width,
	y=(float)bitmap.rows / (float)height;

	glBegin(GL_QUADS);
	glTexCoord2d(0,0); glVertex2f(0,bitmap.rows);
	glTexCoord2d(0,y); glVertex2f(0,0);
	glTexCoord2d(x,y); glVertex2f(bitmap.width,0);
	glTexCoord2d(x,0); glVertex2f(bitmap.width,bitmap.rows);
	glEnd();
	glPopMatrix();
	glTranslatef(face->glyph->advance.x >> 6 ,0,0);

	glEndList();

	return true;
}

void font_data::load() {
    error::log(error::con_only,"Loading font %s...",font_name.c_str());

	texlist = new GLuint[256];

	FT_Library library;
	if (FT_Init_FreeType( &library )){
	    error::log(error::con_only,"error\n");
        error::log(error::log_err,"font %s: error loading freefont",font_name.c_str());
        return;
	}

	FT_Face face;

	if (FT_New_Face( library, font_name.c_str(), 0, &face )){
	    error::log(error::con_only,"error\n");
        error::log(error::log_err,"font %s: error loading file",font_name.c_str());
        return;
	}

    FT_Set_Pixel_Sizes(face,0,h);

	dispbase = glGenLists(256);
	glGenTextures( 256, texlist );

	for(unsigned int i=0;i<256;i++){
		if( !make_dlist(face,i,dispbase,texlist) ){
            error::log(error::log_err,"in font %s",font_name.c_str());
            return;
		}
	}

	FT_Done_Face(face);

	FT_Done_FreeType(library);

	error::log(error::con_only,"Done\n",font_name.c_str());
}

void font_data::unload() {
	glDeleteLists(dispbase,256);
	glDeleteTextures(256,texlist);
	delete[] texlist;
}

inline void pushScreenCoordinateMatrix() {
	glPushAttrib(GL_TRANSFORM_BIT);
	GLint   viewport[4];
	glGetIntegerv(GL_VIEWPORT, viewport);
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]);
	glPopAttrib();
}

inline void pop_projection_matrix() {
	glPushAttrib(GL_TRANSFORM_BIT);
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glPopAttrib();
}

void font_data::print( float x, float y, const char *fmt, ...)  {

	pushScreenCoordinateMatrix();

	GLuint font = dispbase;
	float h = this->h/.9f;
	char text[1024];
	va_list	ap;

	if (fmt == NULL) *text=0;
	else {
		va_start(ap, fmt);
		vsprintf(text, fmt, ap);
		va_end(ap);
	}

	const char *start_line=text;
	vector<string> lines;
	const char *c;
	for(c=text;*c;c++) {
		if(*c=='\n') {
			string line;
			for(const char *n=start_line;n<c;n++) line.append(1,*n);
			lines.push_back(line);
			start_line=c+1;
		}
	}
	if(start_line) {
		string line;
		const char* n;
		for(n=start_line;n<c;n++) line.append(1,*n);
		lines.push_back(line);
	}

	glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT);
	glMatrixMode(GL_MODELVIEW);
	glDisable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

	glListBase(font);

	float modelview_matrix[16];
	glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix);

	for(unsigned int i=0;i<lines.size();i++) {
		glPushMatrix();
		glLoadIdentity();
		glTranslatef(x,y-h*i,0);
		glMultMatrixf(modelview_matrix);

		// glRasterPos2f(0,0);
		glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());
		// float rpos[4];
		// glGetFloatv(GL_CURRENT_RASTER_POSITION ,rpos);
		// float len=x-rpos[0]; (Assuming No Rotations Have Happend)

		glPopMatrix();
	}

	glPopAttrib();

	pop_projection_matrix();
}

}

